package com.lauor.smpedr.session;

import com.lauor.smpedr.Configuration;
import com.lauor.smpedr.MethodSignature;
import com.lauor.smpedr.plugin.InterceptorScopeEnum;
import com.lauor.smpedr.plugin.InterceptorWrapper;
import com.lauor.smpedr.plugin.Invocation;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Sql Session代理
 */
public class SqlSessionProxy implements InvocationHandler {

    private SqlSession sqlSession;

    private List<String> sqlSessionMethodNames;

    public SqlSessionProxy(SqlSession sqlSession) {
        this.sqlSession = sqlSession;
        this.sqlSessionMethodNames = Arrays.asList( SqlSession.class.getDeclaredMethods() ).stream()
                .filter(method -> Modifier.isPublic( method.getModifiers() ) )
                .map(method -> method.getName())
                .collect(Collectors.toList());
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //执行拦截器
        if ( !invokeAround(method, args, true) ) {
            return null;
        }
        //设置方法信息
        String methodName = method.getName();
        if ( sqlSessionMethodNames.contains(methodName) ){
            Configuration configuration = sqlSession.getConfiguration();
            if ( configuration.getMethodSignature(methodName) == null){
                configuration.addMethodSignature(methodName, new MethodSignature(method));
            }
        }

        Object result = null;
        try {
            result = method.invoke(this.sqlSession, args);
        } catch (Throwable throwable){
            if (throwable instanceof InvocationTargetException){
                throw ((InvocationTargetException) throwable).getTargetException();
            }
            throw throwable;
        }

        //执行拦截器
        invokeAround(method, args, false);
        return result;
    }

    private boolean invokeAround(Method method, Object[] args, boolean before){
        Configuration configuration = sqlSession.getConfiguration();
        List<InterceptorWrapper> interceptorList = configuration.getInterceptorList();

        for (InterceptorWrapper interceptor : interceptorList) {
            Optional<List<InterceptorScopeEnum>> scopeEnums = interceptor.getScopes();
            if (scopeEnums.isEmpty() || !scopeEnums.get().contains( InterceptorScopeEnum.SQL_SESSION )) {
                continue;
            }
            //校验方法名
            Optional<Set<String>> methods = interceptor.getTargetMethods();
            if (methods.isEmpty() || !methods.get().contains( method.getName() )){
                continue;
            }

            if (before){
                boolean flag = interceptor.getMeta().before( new Invocation(method, args) );
                if (!flag) {
                    return false;
                }
            } else {
                interceptor.getMeta().after( new Invocation(method, args) );
            }
        }
        return true;
    }

}
